<template>
  <div class="modal" v-if="visible" :style="transformStyle">
    <div class="modal-title" ref="modalTitleRef">
      {{ title }}
      <close-outlined class="icon" @click="handelClose" />
    </div>
    <div class="modal-content">
      <slot></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'Modal',
})
</script>
<script setup lang="ts">
import { ref, computed, CSSProperties, watch, watchEffect, defineProps, defineEmits } from 'vue'
import { useDraggable } from '@vueuse/core'
defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
  title: {
    type: String,
    default: '',
  },
})
const emits = defineEmits(['update:visible'])
const modalTitleRef = ref<HTMLElement>()

const { x, y, isDragging } = useDraggable(modalTitleRef)

const startX = ref<number>(0)
const startY = ref<number>(0)
const startedDrag = ref(false)
const transformX = ref(0)
const transformY = ref(0)
const preTransformX = ref(0)
const preTransformY = ref(0)
const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 })
watch([x, y], () => {
  if (!startedDrag.value) {
    startX.value = x.value
    startY.value = y.value
    const bodyRect = document.body.getBoundingClientRect()
    dragRect.value.right = bodyRect.width
    dragRect.value.bottom = bodyRect.height
    preTransformX.value = transformX.value
    preTransformY.value = transformY.value
  }
  startedDrag.value = true
})
watch(isDragging, () => {
  if (!isDragging) {
    startedDrag.value = false
  }
})

watchEffect(() => {
  if (startedDrag.value) {
    transformX.value =
      preTransformX.value +
      Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
      startX.value
    transformY.value =
      preTransformY.value +
      Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
      startY.value
  }
})
const transformStyle = computed<CSSProperties>(() => {
  return {
    transform: `translate(${transformX.value}px, ${transformY.value}px)`,
  }
})
const handelClose = () => emits('update:visible', false)
</script>

<style lang="less" scoped>
.modal {
  position: absolute;
  width: 450px;
  height: 300px;
  left: calc(50% - 150px);
  top: calc(50% - 225px);
  overflow: hidden;
  background-color: #fff;
  border-radius: 2px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}

.modal-title {
  font-size: 16px;
  padding: 16px 24px;
  font-weight: bold;
  line-height: 16px;
  position: relative;
  color: #fff;
  background-color: #3e456b !important;
  cursor: move;
  .icon {
    position: absolute;
    top: 15px;
    right: 10px;
    z-index: 999;
    cursor: pointer;
  }
}
.modal-content {
  padding: 12px;
  height: 235px;
  width: 100%;
  overflow: auto;
}
</style>
